package com.dddframework.core.contract;

import com.dddframework.core.context.BaseContext;
import com.dddframework.core.utils.BizAssert;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.util.List;
import java.util.Map;

/**
 * 基础查询，PO依赖此类用于条件查询数据库
 * <p>
 * 注意子类不要添加@Accessors(chain = true)注解，否则会导致后面BeanKit#toMap(Object, Map, Collection) 获取不到属性
 *
 * @author Jensen
 * @公众号 架构师修行录
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Query implements Serializable {
    private static final long serialVersionUID = -5283460708108514233L;
    // 查询条件后缀：以该后缀结尾的参数，可以自动补充查询条件
    public static final String START_QUERY = "Start";
    public static final String END_QUERY = "End";
    public static final String NOT_QUERY = "Not";
    public static final String IN_QUERY = "In";
    public static final String NOT_IN_QUERY = "NotIn";
    public static final String LIKE_QUERY = "Like";
    public static final String LIKE_LEFT_QUERY = "LikeLeft";
    public static final String LIKE_RIGHT_QUERY = "LikeRight";
    public static final String MIN_QUERY = "Min";
    public static final String MAX_QUERY = "Max";
    public static final String MIN_EQUALS_QUERY = "MinEq";
    public static final String MAX_EQUALS_QUERY = "MaxEq";
    public static final String IS_NULL_QUERY = "IsNull";
    // 关键字查询
    public static final String KEYWORDS_QUERY = "keywords";
    // select字段列表
    protected String[] select;
    // 分组字段列表
    protected String groupBy;
    // having过滤条件
    protected String having;
    // 排序字段列表
    protected List<SortParam> sorts;
    // 关键字：or查询
    protected Map<String, Object> keywords;
    // 当前页码：从1开始，默认1
    protected Long current = 1L;
    // 每页显示记录数：默认10
    protected Long size = 10L;

    public Query select(String... columns) {
        this.setSelect(columns);
        return this;
    }

    public Query groupBy(String groupBy) {
        this.setGroupBy(groupBy);
        return this;
    }

    public Query having(String having) {
        this.setHaving(having);
        return this;
    }

    public Query orderBy(List<SortParam> sorts) {
        this.setSorts(sorts);
        return this;
    }

    @JsonIgnore
    public long getStartIndex() {
        return (this.current - 1L) * this.size;
    }

    @JsonIgnore
    public long getEndIndex() {
        return this.getStartIndex() + this.size;
    }

    @Deprecated
    public Long getPage() {
        return this.getCurrent();
    }

    @Deprecated
    public void setPage(Long page) {
        this.setCurrent(page);
    }

    @Deprecated
    public Long getLimit() {
        return this.getSize();
    }

    @Deprecated
    public void setLimit(Long limit) {
        this.setSize(limit);
    }

    public <T extends BaseRepository<D, Q>, D extends Model, Q extends Query> T repository() {
        return BaseContext.get(this.getClass());
    }

    public List<Map<String, Object>> query() {
        return repository().query(this);
    }

    public <M extends Model> M one() {
        return (M) repository().one(this);
    }

    public <M extends Model> M one(String errorMsg, Object... params) {
        M one = one();
        return BizAssert.notNull(one, errorMsg, params);
    }

    public <M extends Model> M first() {
        return (M) repository().first(this);
    }

    public <M extends Model> M first(String errorMsg, Object... params) {
        M first = first();
        BizAssert.notNull(first, errorMsg, params);
        return first;
    }

    public int count() {
        return repository().count(this);
    }

    public boolean exist() {
        return repository().exist(this);
    }

    public void exist(String errorMsg, Object... params) {
        BizAssert.isTrue(exist(), errorMsg, params);
    }

    public boolean notExist() {
        return !this.exist();
    }

    public void notExist(String errorMsg, Object... params) {
        BizAssert.isTrue(notExist(), errorMsg, params);
    }

    public <M extends Model> Page<M> page() {
        return (Page<M>) repository().page(this);
    }

    public <M extends Model> Page<M> page(String errorMsg, Object... params) {
        Page<M> page = page();
        BizAssert.notEmpty(page.getRecords(), errorMsg, params);
        return page;
    }

    public <M extends Model> List<M> list() {
        return (List<M>) repository().list(this);
    }

    public <M extends Model> List<M> list(String errorMsg, Object... params) {
        List<M> list = list();
        BizAssert.notEmpty(list, errorMsg, params);
        return list;
    }

}